import * as THREE from 'three'
import * as d3 from 'd3'
// import china from './china.json''
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"


export default class threeTS {
    scene!: THREE.Scene;
    camera!: THREE.PerspectiveCamera;
    renderer!: THREE.WebGLRenderer;
    ambientLight!: THREE.AmbientLight;
    mesh!: THREE.Mesh;
    map!: THREE.Object3D;
    controller!: OrbitControls;
    raycaster!: THREE.Raycaster;
    mouse!: THREE.Vector2;
    textDOM!: HTMLElement;
    lastPick: any;
    constructor() {
        this.init()
    };
    // 初始化
    init(): void {
        // 第一步新建一个场景
        this.scene = new THREE.Scene();
        this.setCamera();
        this.setRenderer();
        this.loadMapData();
        this.addHelper();
        this.setController();
        this.setRaycaster();
        this.animate();
        this.render();
    };
    // 新建透视相机
    setCamera(): void {
        // 第二参数就是 长度和宽度比 默认采用浏览器  返回以像素为单位的窗口的内部宽度和高度
        this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        this.camera.position.z = 75;
    };
    setRenderer(): void {
        this.renderer = new THREE.WebGL1Renderer();
        // 设置画布大小
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        // 这里 其实就是 canvas画布 renderer.domElement
        document.body.appendChild(this.renderer.domElement)
    }
    // 加载地图数据
    loadMapData() {
        // loader 方法加载
        const loader = new THREE.FileLoader()
        THREE.Cache.enabled = true;
        loader.load('three/china.json', (json: any) => {
            const data = JSON.parse(JSON.stringify(json))
            console.log(data);
            this.generateGeometry(JSON.parse(json))
        })
    };
    // 创建动画
    animate(): void {
        /* 动画执行函数 */
        requestAnimationFrame(this.animate.bind(this))
        // 通过摄像机和鼠标位置更新射线
        this.raycaster.setFromCamera(this.mouse, this.camera)
        const intersects = this.raycaster.intersectObjects(this.scene.children, true)
        // 恢复上一次清空的
        if (this.lastPick) {
            this.lastPick.object.material[0].color.set('#2defff')
            this.lastPick.object.material[1].color.set('#3480C4')
        }
        this.lastPick = null
        // 临时储存
        const data = intersects.find((item: any) => item.object.material && item.object.material.length === 2)
        this.lastPick = data
        if (this.lastPick) {
            this.showText();
            this.lastPick.object.material[0].color.set(0xC0C0C0)
            this.lastPick.object.material[1].color.set(0xC0C0C0)
        }
        this.render()
    };
    // 生成地图几何体
    generateGeometry(res: any): void {
        const mapData = res
        // const mapData = JSON.parse(res)
        // 墨卡托投影转换
        const projection = d3.geoMercator().center([104.0, 37.5]).translate([0, 0])
        // 初始化一个地图对象
        this.map = new THREE.Object3D()
        // 一级 获取数据节点
        mapData.features.forEach((el: any) => {
            // 每个的坐标数组
            const coordinates = el.geometry.coordinates
            //  定一个省份3D对象
            const province: any = new THREE.Object3D()
            // 将省份的属性加进来
            province.properties = el.properties
            // 循环坐标数组
            coordinates.forEach((multiPolygon: any) => {
                multiPolygon.forEach((polygon: any) => {
                    // 省级渲染动画
                    const pointsArray = new Array()
                    const shape = new THREE.Shape()
                    const lineMaterial = new THREE.LineBasicMaterial({ color: 'white', })
                    const lineGeometry = new THREE.BufferGeometry()

                    for (let i = 0; i < polygon.length; i++) {
                        const [x, y] = projection(polygon[i])
                        if (i === 0) { shape.moveTo(x, -y) }
                        shape.lineTo(x, -y)
                        pointsArray.push(new THREE.Vector3(x, y, 5))
                    }

                    const extrudeSettings = { depth: 10, bevelEnabled: false, }
                    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)
                    const material = new THREE.MeshBasicMaterial({ color: '#2defff', transparent: true, opacity: 0.6, })
                    const material1 = new THREE.MeshBasicMaterial({ color: '#3480C4', transparent: true, opacity: 0.5, })
                    const mesh = new THREE.Mesh(geometry, [material, material1])
                    const line = new THREE.Line(lineGeometry, lineMaterial)
                    province.add(mesh)
                    province.add(line)
                })
            })
            this.map.add(province)
            this.scene.add(this.map)
        })
    };
    // 相机辅助视图
    addHelper() {
        const helper = new THREE.CameraHelper(this.camera)
        this.scene.add(helper)
    };
    // 交互控制器
    setController() {
        this.controller = new OrbitControls(this.camera, this.renderer.domElement)
        this.camera.position.set(0, 10, 100);
        this.controller.update();
    }
    setRaycaster() {
        this.raycaster = new THREE.Raycaster()
        this.mouse = new THREE.Vector2()
        // 获取文字展示DOM
        this.textDOM = document.getElementById('text') as HTMLElement
        const onMouseMove = (event: any) => {
            // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
            this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1
            this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
            // DOW文字展示位置
            this.textDOM.style.left = event.clientX + 2 + 'px'
            this.textDOM.style.top = event.clientY + 2 + 'px'
        }

        window.addEventListener('mousemove', onMouseMove)
    }
    // 文字展示
    showText() {
        // 显示省份的信息
        if (this.lastPick) {
            const properties = this.lastPick.object.parent.properties
            this.textDOM.textContent = properties.name
            this.textDOM.style.visibility = 'visible'
        } else {
            this.textDOM.style.visibility = 'hidden'
        }
    }
    // 渲染器
    render(): void {
        this.renderer.render(this.scene, this.camera)
    }
}
